Skip to content

06 记忆系统面试题

1、什么是短期记忆?

短期记忆指的是 Agent 在一次会话或一次任务执行过程中保存的上下文信息。

比如用户刚才问了什么、模型已经回答了什么、调用过哪些工具、工具返回了什么结果,这些都属于短期记忆。

它的作用是让 Agent 能够理解当前对话的上下文,而不是每一轮都像第一次见到用户一样。

举个例子:

text
用户:帮我查一下北京今天的天气
助手:北京今天晴,最高温 28 度
用户:那明天呢?

第二句话里的“那”指的是北京天气,如果没有短期记忆,模型就不知道用户在问什么。

在 LangGraph v1.0 里,短期记忆通常通过 checkpointer 实现。每个线程都有自己的 thread_id,LangGraph 会把状态保存下来,下一轮调用时再恢复出来。

简单理解就是:

text
thread_id = 一段会话
state = 当前会话状态
checkpointer = 保存和恢复 state 的组件

LangChain v1.0 的 create_agent 底层也是基于 LangGraph 构建的,所以它的会话记忆也是依赖 LangGraph 的状态持久化能力。

2、什么是长期记忆?

长期记忆指的是可以跨会话、跨任务长期保存的信息。

它不是为了记录每一句对话,而是保存对以后有价值的信息,例如:

  • 用户偏好
  • 用户画像
  • 常用配置
  • 历史决策
  • 项目背景
  • 重要事实

比如用户多次提到“我喜欢回答简洁一点”,这个信息就适合进入长期记忆。下次用户重新打开一个新会话,系统仍然可以根据这个偏好调整回答方式。

短期记忆更像“这次对话的上下文”,长期记忆更像“对这个用户或业务的长期了解”。

在 LangGraph v1.0 里,长期记忆通常通过 Store 实现。Checkpointer 负责保存线程内的状态,Store 负责保存跨线程、跨会话的数据。

可以把两者区分成:

text
短期记忆:thread_id 维度,跟当前会话绑定
长期记忆:user_id / namespace 维度,跟用户或业务对象绑定

3、为什么需要Memory?

Memory 的核心作用是让 Agent 有连续性。

没有 Memory 的系统,每次请求都是独立的。用户刚刚说过的信息,下一轮就丢了;用户长期形成的偏好,也无法沉淀下来。这样的 Agent 很难做复杂任务,只适合一次性问答。

有了 Memory 后,Agent 可以做到几件事:

第一,理解上下文。

用户说“继续”“换一种方式”“刚才那个方案”,系统能知道指的是什么。

第二,减少重复提问。

如果系统已经知道用户的技术栈、语言偏好、业务背景,就不需要每次都重新问。

第三,支持复杂任务。

很多 Agent 任务不是一轮完成的,比如写代码、查资料、做分析、执行多步流程,都需要保存中间状态。

第四,个性化回答。

不同用户关注点不同,有的人要原理,有的人要代码,有的人要结论。长期记忆可以让系统逐渐适应用户。

所以 Memory 不是简单地“保存聊天记录”,而是 Agent 从一次性工具变成长期助手的基础能力。

4、为什么不能直接把所有历史记录放进Prompt?

最直接的原因是上下文窗口有限。

大模型能接收的 Prompt 长度是有限的。历史记录越来越多后,不可能无限塞进去。即使模型支持很长上下文,也不代表应该全部放进去。

主要问题有几个。

第一,成本高。

Prompt 越长,调用成本越高,响应也越慢。很多历史内容其实对当前问题没有帮助,全部传进去是在浪费。

第二,噪声多。

历史记录里有大量无关信息,模型可能被干扰,反而抓不住当前问题的重点。

第三,容易产生冲突。

用户早期说过的话可能已经过期,比如以前用 MySQL,现在改成 PostgreSQL。如果全部放进去,模型可能不知道应该信哪一条。

第四,隐私和安全风险更高。

不是所有历史内容都应该反复进入模型上下文,尤其是包含敏感信息时。

更合理的做法是:

text
完整历史记录可以存起来
但每次只召回当前任务真正需要的部分

也就是通过摘要、检索、过滤和排序,把相关记忆放进 Prompt,而不是把所有历史原样拼进去。

5、如何设计Memory架构?

一个比较实用的 Memory 架构,一般会分成四层。

第一层是会话状态层。

保存当前对话的消息、工具调用结果、中间变量等。它解决的是“当前任务进行到哪里了”。在 LangGraph v1.0 里,这一层通常用 checkpointer。

第二层是长期存储层。

保存跨会话的信息,比如用户偏好、用户画像、项目背景、业务事实等。在 LangGraph v1.0 里,这一层通常用 Store。

第三层是检索层。

当用户发起新请求时,系统需要从长期记忆中找出相关内容。这里可以用关键词检索、向量检索、标签过滤,也可以组合使用。

第四层是写入和治理层。

不是所有内容都应该写入记忆。系统要判断哪些信息值得保存,哪些信息应该更新,哪些信息已经过期,哪些信息不能保存。

整体流程可以这样理解:

text
用户输入

读取短期记忆(当前会话状态)

召回长期记忆(用户偏好、历史事实)

组装 Prompt

模型生成回答 / 调用工具

更新短期记忆

必要时写入长期记忆

这里最重要的是分清边界:

text
短期记忆解决当前会话连续性
长期记忆解决跨会话连续性
检索层解决用什么记忆
治理层解决什么该记、什么不该记

6、如何实现会话记忆?

会话记忆的重点是保存每一轮对话后的状态,并在下一轮恢复出来。

如果自己实现,可以用 session_idthread_id 作为 key,把消息列表保存到 Redis、数据库或本地存储中。

简单结构如下:

json
{
  "thread_id": "user-001-chat-001",
  "messages": [
    {"role": "user", "content": "帮我查北京天气"},
    {"role": "assistant", "content": "北京今天晴"}
  ]
}

下一轮用户继续提问时,系统根据 thread_id 取出历史消息,再和新问题一起传给模型。

在 LangGraph v1.0 中,推荐使用 checkpointer 来做这件事。开发时可以用内存版本,生产环境一般会换成数据库版本。

示例思路:

python
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph

checkpointer = InMemorySaver()

app = graph.compile(checkpointer=checkpointer)

config = {
    "configurable": {
        "thread_id": "user-001-chat-001"
    }
}

app.invoke(
    {"messages": [{"role": "user", "content": "你好"}]},
    config=config,
)

同一个 thread_id 再次调用时,LangGraph 会自动恢复之前的状态。

如果用 LangChain v1.0 的 create_agent,也是类似思路:

python
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver

checkpointer = InMemorySaver()

agent = create_agent(
    model="anthropic:claude-sonnet-4-5",
    tools=[],
    checkpointer=checkpointer,
)

agent.invoke(
    {"messages": [{"role": "user", "content": "我叫张三"}]},
    config={"configurable": {"thread_id": "chat-001"}},
)

会话记忆实现时还要注意控制长度。不能无限保存所有消息给模型,通常会配合摘要或窗口截断,只保留最近几轮和关键信息。

7、如何实现跨会话记忆?

跨会话记忆不能只依赖 thread_id,因为 thread_id 通常只代表一段会话。用户开启新会话后,线程就变了,但用户本身没有变。

所以跨会话记忆一般要绑定到 user_idtenant_id 或业务对象 ID 上。

例如:

text
user_id = u_001
namespace = memories/u_001

保存的信息可能是:

json
{
  "preferred_language": "中文",
  "answer_style": "简洁直接",
  "tech_stack": ["Python", "LangGraph", "Redis"]
}

在 LangGraph v1.0 里,跨会话记忆通常用 Store。Checkpointer 管当前线程状态,Store 管长期数据。

大致流程是:

text
新会话开始

根据 user_id 从 Store 查询长期记忆

把相关记忆加入上下文

完成回答

如果发现新的长期偏好,再写回 Store

示例思路:

python
from langgraph.store.memory import InMemoryStore

store = InMemoryStore()

namespace = ("memories", "user-001")

store.put(
    namespace,
    "preference",
    {"answer_style": "简洁直接"},
)

memory = store.get(namespace, "preference")

生产环境里,Store 后面一般会接数据库,比如 PostgreSQL、Redis、ElasticSearch 或向量数据库,具体取决于记忆类型和检索方式。

8、如何实现用户画像?

用户画像不是把用户所有聊天记录堆起来,而是把稳定、有用的信息结构化保存。

常见画像字段包括:

json
{
  "user_id": "u_001",
  "role": "后端开发工程师",
  "language": "中文",
  "tech_stack": ["Python", "FastAPI", "LangGraph"],
  "answer_preference": "先给结论,再解释原因",
  "business_context": "正在做企业知识库问答系统"
}

画像的来源一般有三种。

第一,用户明确告诉系统。

例如:“以后都用中文回答”“我是 Java 后端”。这种信息可信度比较高,可以直接写入或经过确认后写入。

第二,从多轮对话中归纳。

比如用户多次要求“别写太复杂”,系统可以归纳出用户偏好简洁回答。但这种信息最好不要太武断,需要保留证据或置信度。

第三,从业务系统同步。

例如用户的角色、部门、权限、购买套餐等,这类信息应该来自业务数据库,而不是让模型猜。

实现时要注意两点。

第一,画像要结构化。

不要只保存一段长文本,否则后面很难检索、更新和判断冲突。

第二,画像要可更新。

用户偏好会变,技术栈也会变。画像最好带上更新时间和来源。

json
{
  "key": "answer_preference",
  "value": "简洁直接",
  "source": "user_explicit",
  "updated_at": "2026-06-16"
}

这样后续如果出现冲突,可以优先相信更新、更可靠的信息。

9、如何实现长期记忆存储?

长期记忆存储要先看记忆类型,不同类型适合不同存储方式。

如果是用户偏好、用户画像、配置项,适合用关系型数据库或 KV 存储,因为它们结构清晰,经常按 key 查询。

例如:

text
user_id + memory_key -> memory_value

如果是历史经验、文档片段、对话摘要这类非结构化内容,适合用向量数据库,因为后续通常是按语义相似度召回。

如果是短期状态或高频读写的缓存,可以用 Redis。

一个常见设计是:

text
PostgreSQL:保存结构化长期记忆
Redis:保存会话缓存和热点记忆
Vector DB:保存可语义检索的文本记忆
Object Storage:保存大文件或原始材料

长期记忆表可以设计成这样:

sql
CREATE TABLE agent_memories (
    id BIGSERIAL PRIMARY KEY,
    user_id TEXT NOT NULL,
    memory_type TEXT NOT NULL,
    memory_key TEXT,
    content TEXT NOT NULL,
    metadata JSONB,
    importance INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

如果需要向量检索,可以增加 embedding 字段,或者同步到专门的向量库。

长期记忆存储还要考虑删除能力。用户要求删除记忆时,系统必须能找到并删除,而不是散落在各处无法清理。

10、如何实现记忆召回?

记忆召回就是在用户发起请求时,从记忆库里找出当前问题真正需要的内容。

常见做法有三种。

第一,规则召回。

根据 user_idmemory_type、标签等条件直接查询。

例如用户画像、语言偏好、权限信息,这类不需要向量检索,直接按 key 查就行。

第二,关键词召回。

适合查找包含明确关键词的记忆,比如项目名、产品名、错误码。

第三,向量召回。

适合语义相似的场景。比如用户问“上次那个接口超时的问题”,不一定包含原始记忆里的关键词,但语义接近,可以用 embedding 检索。

实际项目中通常会混合使用:

text
用户问题

查用户画像和固定偏好

做关键词召回

做向量召回

合并结果

过滤、排序、去重

放入 Prompt

召回后不要直接全部塞给模型,还要做排序。一般会考虑:

  • 相关性
  • 重要性
  • 新旧程度
  • 可信来源
  • 是否和当前任务冲突

最终只选少量最有用的记忆进入上下文。

11、如何避免无关记忆被召回?

避免无关记忆被召回,关键不是只靠向量相似度。

向量检索会找“语义相似”的内容,但语义相似不代表当前任务需要。比如用户问“Python 内存优化”,可能召回到“用户喜欢简洁回答”之外的一堆历史 Python 讨论,但很多都没用。

常见处理方式有几个。

第一,加过滤条件。

召回前先按用户、租户、项目、记忆类型过滤,避免跨用户、跨项目召回。

text
user_id = 当前用户
project_id = 当前项目
memory_type in [preference, fact, summary]

第二,给记忆打标签。

保存时就标好类型,例如:

text
preference:用户偏好
profile:用户画像
project:项目背景
task:任务摘要
fact:事实信息

召回时按类型取,不要混在一起。

第三,设置相似度阈值。

低于阈值的向量结果直接丢弃,避免硬凑结果。

第四,做二次重排。

先召回一批候选,再用 reranker 或 LLM 判断哪些真的和当前问题相关。

第五,限制数量。

即使召回了很多,也只取最相关的几条。记忆不是越多越好,放太多反而影响模型判断。

最后还要处理过期记忆。比如用户以前说用 Vue,后来项目改成 React,旧记忆如果不更新,就会误导模型。

12、如何做Memory压缩?

Memory 压缩的目的,是在不丢掉关键信息的前提下,减少上下文长度和存储成本。

常见压缩方式有几种。

第一,窗口截断。

只保留最近 N 轮对话,适合简单聊天场景。

text
只保留最近 10 条消息
更早的消息不直接进入 Prompt

第二,摘要压缩。

把早期对话总结成一段摘要,替代完整消息。

text
前 30 轮对话 -> 一段会话摘要
最近 5 轮对话 -> 原文保留

第三,结构化提取。

从对话中提取稳定信息,比如用户偏好、项目背景、关键决策,然后保存成结构化字段。

json
{
  "preference": "回答要简洁",
  "project": "企业知识库问答系统",
  "decision": "使用 LangGraph 编排 Agent 流程"
}

第四,去重合并。

如果多条记忆表达的是同一件事,就合并成一条,避免重复召回。

第五,按重要性保留。

不是所有内容都同等重要。临时闲聊可以丢弃,明确偏好、关键业务事实、长期决策应该保留。

在 LangGraph 里,常见做法是把完整状态交给 checkpointer 保存,但进入模型上下文前做裁剪或摘要,而不是把 checkpoint 里的所有内容都传给模型。

13、如何做Memory摘要?

Memory 摘要就是把一段长对话压缩成短文本,同时保留对后续有用的信息。

一个好的摘要不应该只是流水账,而应该提取这些内容:

  • 用户目标
  • 已经确认的事实
  • 已经做过的操作
  • 关键结论
  • 未完成事项
  • 用户偏好

例如一段对话很长,摘要可以写成:

text
用户正在设计一个基于 LangGraph 的客服 Agent。
已确认使用 PostgreSQL 保存结构化记忆,使用向量库保存历史问题摘要。
用户偏好中文回答,要求示例代码尽量简单。
当前还没有决定是否引入 Redis。

实现时通常有两种方式。

第一,定期摘要。

每隔固定轮数,比如 10 轮,对之前消息做一次摘要。

第二,超过阈值后摘要。

当上下文长度超过限制时,把较早的消息压缩成摘要。

摘要时要注意不要过度加工。模型不能把不确定的信息写成确定事实,也不能把用户没有表达过的偏好强行总结出来。

比较稳妥的 Prompt 可以这样写:

text
请总结以下对话中对后续任务有用的信息。
只保留明确出现的信息,不要推测。
按:用户目标、已确认事实、待办事项、用户偏好 四类输出。

摘要生成后,可以替换旧消息,也可以作为长期记忆的一部分保存。

14、Redis在Memory体系中的作用是什么?

Redis 在 Memory 体系里主要适合做高频、短期、低延迟的存储。

常见用途有几个。

第一,保存会话缓存。

比如当前会话的最近消息、临时状态、中间结果。Redis 读写快,适合频繁访问。

第二,保存热点记忆。

有些长期记忆虽然存放在数据库里,但访问非常频繁,可以缓存到 Redis,减少数据库压力。

第三,做过期控制。

Redis 天然支持 TTL,适合保存有生命周期的记忆。

text
临时验证码:5分钟过期
会话状态:24小时过期
任务中间结果:1小时过期

第四,做分布式锁或任务状态。

多个 Agent 实例同时处理同一个用户或任务时,可以用 Redis 控制并发,避免状态被覆盖。

但 Redis 不适合保存所有长期记忆。原因是它主要是内存型存储,成本较高,而且复杂检索能力有限。

比较合理的分工是:

text
Redis:短期状态、热点缓存、临时结果
数据库:结构化长期记忆
向量数据库:语义检索记忆

所以 Redis 在 Memory 系统里更像加速层和临时状态层,而不是唯一的记忆库。

15、向量数据库在Memory体系中的作用是什么?

向量数据库的主要作用是做语义检索。

普通数据库擅长按字段查,比如:

text
user_id = u_001
memory_type = preference

但很多记忆不是靠精确字段能查出来的。

比如用户问:

text
上次我们讨论的那个登录超时问题,后来怎么定的?

这句话里可能没有出现原始记忆中的关键词。如果只靠关键词搜索,可能查不到。但向量检索可以根据语义相似度,把相关的历史摘要找出来。

向量数据库一般会保存三类信息:

json
{
  "id": "mem_001",
  "content": "用户之前讨论过登录接口超时,决定先增加超时日志,再排查数据库慢查询。",
  "embedding": [0.12, 0.34, ...],
  "metadata": {
    "user_id": "u_001",
    "project_id": "p_001",
    "memory_type": "task_summary",
    "created_at": "2026-06-16"
  }
}

检索流程通常是:

text
用户问题

生成 query embedding

向量库相似度搜索

按 metadata 过滤

取 TopK 结果

必要时重排

放入 Prompt

向量数据库适合存对话摘要、历史问题、文档片段、经验记录等非结构化内容。

但它不适合替代所有存储。像用户语言偏好、权限、开关配置这些精确数据,还是应该放在关系型数据库或 KV 存储中。

一句话总结:向量数据库解决的是“意思相近但关键词不一定相同”的记忆召回问题。